#include "vector.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

MyVector *myVectorNew(int capacity) {
    int trueCapacity = myVectorDetermineCapacity(capacity);
    MyVector *vecPtr = (MyVector *) malloc(sizeof(MyVector));
    checkAddress(vecPtr);
    vecPtr->size = 0;
    vecPtr->capacity = trueCapacity;
    vecPtr->data = (int *) malloc(trueCapacity * sizeof(int));
    checkAddress(vecPtr->data);
    return vecPtr;
}

int myVectorDetermineCapacity(int capacity) {
    if (capacity < 1) {
        exit(EXIT_FAILURE);
    }
    int trueCapacity = minCapacity;
    while (capacity > trueCapacity / growFactor) {
        trueCapacity *= growFactor;
    }
    return trueCapacity;
}

int myVectorSize(MyVector *vecPtr) {
    return vecPtr->size;
}

int myVectorCapacity(MyVector *vecPtr) {
    return vecPtr->capacity;
}

bool isMyVectorEmpty(MyVector *vecPtr){
    return vecPtr->size == 0;
}

int myVectorAt(MyVector *vecPtr, int i){
    if (i < 0 || i > vecPtr->size - 1) {
        exit(EXIT_FAILURE);
    }
    return *(vecPtr->data + i);
}

void myVectorPush(MyVector *vecPtr, int n) {
    myVectorResizeForSize(vecPtr, vecPtr->size + 1);

    *(vecPtr->data + vecPtr->size) = n;
    // vecPtr->size += 1 also works
    ++(vecPtr->size);
}

void myVectorInsert(MyVector *vecPtr, int index, int value) {
    if (index < 0 || index > vecPtr->size) {
        exit(EXIT_FAILURE);
    }
    myVectorResizeForSize(vecPtr, vecPtr->size + 1);

    // shift items to the right
    memmove(vecPtr->data + index + 1, vecPtr->data + index, (vecPtr->size - index) * sizeof(int));
    // insert item
    *(vecPtr->data + index) = value;
    vecPtr->size += 1;
}

void myVectorPrepend(MyVector *vecPtr, int n) {
    myVectorInsert(vecPtr, 0, n);
}

int myVectorPop(MyVector *vecPtr) {
    if (vecPtr->size == 0) {
        exit(EXIT_FAILURE);
    }
    myVectorResizeForSize(vecPtr, vecPtr->size - 1);
    int res = *(vecPtr->data + vecPtr->size -1);
    (vecPtr->size)--;
    return res;
}

void myVectorDelete(MyVector *vecPtr, int index) {
    if (index < 0 || index > vecPtr->size - 1) {
        exit(EXIT_FAILURE);
    }
    myVectorResizeForSize(vecPtr, vecPtr->size - 1);
    memmove(vecPtr->data + index, vecPtr->data + index + 1, (vecPtr->size - index)*sizeof(int));
    (vecPtr->size)--;
}

void myVectorRemove(MyVector *vecPtr, int value) {
    for (int i = 0; i < vecPtr->size; i++) {
        if (*(vecPtr->data + i) == value) {
            myVectorDelete(vecPtr, i);
            i--;
        }
    }
}

int myVectorFind(MyVector *vecPtr, int value) {
    for (int i = 0; i < vecPtr->size; i++) {
        if (*(vecPtr->data + i) == value) {
            return i;
        }
    }
    return -1;
}

void checkAddress(void *p) {
    if (p == NULL) {
        printf("Unable to allocate memory.\n");
        exit(EXIT_FAILURE);
    }
}

void myVectorResizeForSize(MyVector *vecPtr, int size) {
    if (size >= vecPtr->capacity) {
        myVectorUpsize(vecPtr);
    } else if (size <= (vecPtr->capacity)/shrinkFactor) {
        myVectorDownsize(vecPtr);
    }
}

void myVectorUpsize(MyVector *vecPtr) {
    int *newData = (int *) realloc(vecPtr->data, (vecPtr->capacity * growFactor) * sizeof(int));
    checkAddress(newData);
    vecPtr->data = newData;
    vecPtr->capacity *= growFactor;
}

void myVectorDownsize(MyVector *vecPtr) {
    int oldCapacity = vecPtr->capacity;
    int newCapacity = vecPtr->capacity / growFactor;

    if (newCapacity < minCapacity) {
        newCapacity = minCapacity;
    }

    if (oldCapacity != newCapacity) {
        int *newData = (int *) realloc(vecPtr->data, newCapacity * sizeof(int));
        checkAddress(newData);
        vecPtr->data = newData;
        vecPtr->capacity = newCapacity;
    }
}

